package tiers3.services;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import tiers3.constants.Constantes;
import common.Tweet;
import common.Utilisateur;

public class ServicePersistanceImpl implements IServicePersistance {

	/**
	 * Création d'un nouvel utilisateur
	 */
	@Override
	public Utilisateur creerUtilisateur(Utilisateur pUtilisateur) throws RemoteException {
		try {
			// Attribution d'un id aléatoire à l'utilisateur
			pUtilisateur.setId( new Random().nextInt(99999));
			// Mise à jour de l'utilisateur
			modifierUtilisateur(pUtilisateur);
			// Renvoie l'utilisateur
			return pUtilisateur;
		} catch (IOException e) {
			e.printStackTrace();
		}
		// Renvoie null si l'utilisateur n'a pas pu être créer
		return null;
	}

	/**
	 * Modification d'un utilisateur passé en paramètre
	 */
	@Override
	public Utilisateur modifierUtilisateur(Utilisateur pUtilisateur) throws RemoteException {
		try {
			// Ecriture du fichier xml correspondant à l'utilisateur passé en paramètre
			FileOutputStream fileOutputStream;
			// Le chemin est construit en fonction des constantes définies dans l'application
			// Ainsi que de l'id de l'utilisateur
			fileOutputStream = new FileOutputStream(Constantes.FOLDER
					+ Constantes.UTILISATEUR_PREFIXE_SERIALISATION
					+ pUtilisateur.getId() + Constantes.SUFFIXE_BIN);
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(
					fileOutputStream);
			// Ecriture de la stucture XML détaillée dans la classe Utilisateur
			objectOutputStream.writeObject(pUtilisateur);
			objectOutputStream.flush();
			objectOutputStream.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// Renvoie l'utilisateur
		return pUtilisateur;
	}
	
	/**
	 * Création d'un tweet en fonction de l'utilisateur passé en paramètre
	 */
	@Override
	public Tweet creerTweet(Utilisateur pUtilisateur, Tweet pTweet) throws RemoteException {
		try {
			// Ajout de pTweet à la liste des tweets de l'utilisateur
			pUtilisateur.ajoutTweet(pTweet);
			// Mise à jour de l'utilisateur
			modifierUtilisateur(pUtilisateur);
			// Renvoie le tweet
			return pTweet;
		} catch (IOException e) {
			e.printStackTrace();
		}
		// Renvoie null si le tweet n'a pas pu être créé
		return null;
	}

	/**
	 * Suppression d'un tweet en fonction de l'utilisateur passé en paramètre
	 */
	@Override
	public boolean supprimerTweet(Utilisateur pUtilisateur, Tweet pTweet) throws RemoteException{
		try {
			// Suppression de pTweet de la liste des tweets de l'utilisateur
			pUtilisateur.suppTweet(pTweet);
			// Mise à jour de l'utilisateur
			modifierUtilisateur(pUtilisateur);
			// Renvoie true le tweet a été supprimer
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		}
		// Renvoie false si la suppression du tweet n'a pas été effectuée
		return false;
	}

	/**
	 * Retourne la liste de tous les utilisateurs de l'application
	 */
	@Override
	public List<Utilisateur> rechercherTousUtilisateurs() throws RemoteException {
		// Instanciation d'une liste d'utilisateurs vide par défaut
		List<Utilisateur> maListeUtilisateur = new ArrayList<Utilisateur>();

		// Récupération et vérification du dossier de stockage des utilisateurs
		File repertoire = new File(Constantes.FOLDER);
		assert repertoire.isDirectory();

		// Récupération de l'ensemble des fichier contenus dans le répertoire
		File[] tousLesFichiers = repertoire.listFiles();
		if (tousLesFichiers != null) {
			// Pour chaque fichier du répertoire...
			for (File fichier : tousLesFichiers) {
				if (!fichier.isDirectory()) {
					FileInputStream fileInputStream;
					try {
						fileInputStream = new FileInputStream(fichier.getPath());
						ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
						// ... ajout de l'objet utilisateur dé-sérialisé à la liste des utilisateurs
						maListeUtilisateur.add((Utilisateur) objectInputStream.readObject());
						fileInputStream.close();
						objectInputStream.close();
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				}
			}
		}
		// Renvoie l'ensemble de la liste des utilisateurs
		return maListeUtilisateur;
	}

	/**
	 * Retourne la liste de tous les tweets de l'application
	 */
	@Override
	public List<Tweet> rechercherTousTweets() throws RemoteException {
		List<Tweet> listeTweets = new ArrayList<Tweet>();
		// Récupération de l'ensemble des utilisateurs
		// Puis pour chaque utilisateur récupération de l'ensemble de ses tweets
		for (Utilisateur utilisateur : rechercherTousUtilisateurs()) {
			listeTweets.addAll(utilisateur.getListeTweets());
		}
		// Renvoie l'ensemble de la liste des tweets
		return listeTweets;
	}

	/**
	 * Retourne la liste des tweets correspondant à l'utilisateur passé en paramètre
	 */
	@Override
	public List<Tweet> rechercherTweetsParUtilisateur(Utilisateur pUtilisateur) throws RemoteException {
		// Renvoie la liste des tweets d'un utilisateur passé en paramètre
		return pUtilisateur.getListeTweets();
	}

	/**
	 * Retourne l'utilisateur correspondant à l'id passé en paramètre
	 */
	@Override
	public Utilisateur rechercherUtilisateurParId(int pId) throws RemoteException {
		// Récupération de la liste de l'ensemble des utilisateurs de l'application
		List<Utilisateur> listeUtilisateurs = rechercherTousUtilisateurs();
		// Pour chaque utilisateur on recherche la concordance getId() avec pId
		for (Utilisateur utilisateur : listeUtilisateurs) {
			if (utilisateur.getId() == pId) {
				// Renvoie l'utilisateur correspondant à l'id passé en paramètre
				return utilisateur;
			}
		}
		// Renvoie null si aucun utilisateur n'est trouvé
		return null;
	}

	/**
	 * Retourne l'utilisateur correspondant au couple login/mdp passé en paramètre
	 */
	@Override
	public Utilisateur rechercherUtilisateurParLoginMdp(String pLogin, String pMdp) throws RemoteException {
		// Récupération de l'ensemble des utilisateurs de l'application
		List<Utilisateur> listeUtilisateurs = rechercherTousUtilisateurs();
		// Pour chaque utilisateur on recherche la concordance getLogin()/getMdp() avec pLogin/pMdp
		for (Utilisateur utilisateur : listeUtilisateurs) {
			if (pLogin.equals(utilisateur.getLogin()) && pMdp.equals(utilisateur.getMdp())) {
				// Renvoie l'utilisateur correspondant au couple login/mdp passé en paramètre
				return utilisateur;
			}
		}
		// Renvoie null si aucun utilisateur n'est trouvé
		return null;
	}

	/**
	 * Retourne la liste des utilisateurs correspondants au nom passé en paramètre
	 */
	@Override
	public List<Utilisateur> rechercherUtilisateursParNom(String pNom) throws RemoteException {
		// Récupération de l'ensemble des utilisateurs de l'application
		List<Utilisateur> listeUtilisateurs = rechercherTousUtilisateurs();
		List<Utilisateur> listeRetour = new ArrayList<Utilisateur>();
		// Pour chaque utilisateur on recherche la concordance getNom() avec pNom
		for (Utilisateur utilisateur : listeUtilisateurs) {
			if (pNom.equals(utilisateur.getNom())) {
				listeRetour.add(utilisateur);
			}
		}
		// Renvoie la liste des utilisateurs répondant au nom passé en paramètre
		return listeRetour;
	}

	/**
	 * Retourne la liste des utilisateur correspondants au prénom passé en paramètre
	 */
	@Override
	public List<Utilisateur> rechercherUtilisateursParPrenom(String pPrenom) throws RemoteException {
		// Récupération de l'ensemble des utilisateurs de l'application
		List<Utilisateur> listeUtilisateurs = rechercherTousUtilisateurs();
		List<Utilisateur> listeRetour = new ArrayList<Utilisateur>();
		// Pour chaque utilisateur on recherche la concordance getPrenom() avec pPrenom
		for (Utilisateur utilisateur : listeUtilisateurs) {
			if (pPrenom.equals(utilisateur.getPrenom())) {
				listeRetour.add(utilisateur);
			}
		}
		// Renvoie la liste des utilisateurs correspondant au prenom passé en paramètre
		return listeRetour;
	}
	
	/**
	 * Ajout d'un abonnement utilisateur
	 */
	@Override
	public void ajoutSuivi(Utilisateur pUtilisateur, int pIdSuivi) throws RemoteException {
		try {
			// Ajout d'un abonnement à la liste des abonnements utilisateur
			pUtilisateur.ajoutUtilisateurSuivi(pIdSuivi);
			// Mise à jour de l'utilisateur
			modifierUtilisateur(pUtilisateur);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Suppression d'un abonnement utilisateur
	 */
	@Override
	public void supprimerSuivi(Utilisateur pUtilisateur, int pIdSuivi) throws RemoteException {
		try {
			// Suppression d'un abonnement à la liste des abonnements utilisateur
			pUtilisateur.suppUtilisateurSuivi(pIdSuivi);
			// Mise à jour de l'utilisateur
			modifierUtilisateur(pUtilisateur);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Retourne la liste des abonnement utilisateur (id)
	 */
	@Override
	public List<Integer> rechercheAbonnementUtilisateur(Utilisateur pUtilisateur) throws RemoteException {
		// Retourne la liste des abonnement d'un utilisateur passé en paramètre
		return pUtilisateur.getListeUtilisateurs();
	}
	
	/**
	 * Retourne un utilisateur en fonction de son login
	 */
	@Override
	public Utilisateur rechercherUtilisateurParLogin(String pLogin) throws RemoteException{
		// Récupération de l'ensemble des utilisateurs de l'application
		List<Utilisateur> listeUtilisateurs = rechercherTousUtilisateurs();
		// Pour chaque utilisateur on recherche la concordance getLogin()/getMdp() avec pLogin/pMdp
		for (Utilisateur utilisateur : listeUtilisateurs) {
			if (pLogin.equals(utilisateur.getLogin())) {
				// Renvoie l'utilisateur correspondant au login passé en paramètre
				return utilisateur;
			}
		}
		// Renvoie null si aucun utilisateur ne correspond au login
		return null;
	}
	
}
